/// Qualab - 24/11/2011

#include <wink/app.h>
#include <wink/window.h>
#include <wink/errors.h>
#include <exception>
#include <memory>
#include <map>

namespace
{
    using namespace std;
    using namespace wink;

    HINSTANCE   gInstance;
    LPSTR       gCommandLine;
    int         gShowMode;
    PString     gClassName = TEXT( "winkWindow" );

    map< HWND, Window >  gRegistered;

    LRESULT CALLBACK WindowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
    {
        using namespace std;
        using namespace wink;

        switch( message )
        {
            case WM_DESTROY:
            {
                Window& window = App::Lookup( hWnd );
                window.OnClose();
                window.Reset();
                PostQuitMessage( 0 );
                break;
            }

            case WM_MOVE:
            {
                Window& window = App::Lookup( hWnd );
                window.UpdateRect();
                return window.OnMove();
            }

            case WM_SIZE:
            {
                Window& window = App::Lookup( hWnd );
                window.UpdateRect();
                return window.OnResize();
            }

            case WM_LBUTTONDOWN:
            {
                WORD x = LOWORD( lParam );
                WORD y = HIWORD( lParam );
                Window& window = App::Lookup( hWnd );
                return window.OnMouseLDown( x, y );
            }

        case WM_LBUTTONUP:
             {
                WORD x = LOWORD( lParam );
                WORD y = HIWORD( lParam );
                Window& window = App::Lookup( hWnd );
                return window.OnMouseLUp( x, y );
             }

        default:
            return DefWindowProc( hWnd, message, wParam, lParam );
        }

        return 0;
    }
}

namespace wink
{
    void App::Init()
    {
        WNDCLASSEX wcx;

        wcx.cbSize          = sizeof( WNDCLASSEX );
        wcx.style           = 0;
        wcx.lpfnWndProc     = WindowProc;
        wcx.cbClsExtra      = 0;
        wcx.cbWndExtra      = 0;
        wcx.hInstance       = Instance();
        wcx.hIcon           = LoadIcon( NULL, IDI_APPLICATION );
        wcx.hCursor         = LoadCursor( NULL, IDC_ARROW );
        wcx.hbrBackground   = static_cast< HBRUSH >( GetStockObject( BLACK_BRUSH ) );
        wcx.lpszMenuName    = NULL;
        wcx.lpszClassName   = ClassName();
        wcx.hIconSm         = wcx.hIcon;

        if( !RegisterClassEx( &wcx ) )
            throw ErrorWindowClassNotRegistered();
    }

    void App::Run()
    {
        MSG msg[1];
        while( GetMessage( msg, NULL, 0, 0 ) )
        {
            TranslateMessage( msg );
            DispatchMessage( msg );
        }
    }

    void App::Stop()
    {
        PostQuitMessage( 0 );
    }

    HINSTANCE App::Instance()
    {
        return gInstance;
    }

    LPSTR App::CommandLine()
    {
        return gCommandLine;
    }

    int App::ShowMode()
    {
        return gShowMode;
    }

    PString App::ClassName()
    {
        return gClassName;
    }

    void App::Register( const Window& window )
    {
        if( window.Handle() )
            gRegistered[ window.Handle() ] = window;
    }

    void App::Erase( const Window& window )
    {
        if( window.Handle() )
            gRegistered.erase( window.Handle() );
    }

    Window& App::Lookup( HWND handle )
    {
        auto it = gRegistered.find( handle );
        if( it == gRegistered.end() )
            throw ErrorWindowNotFound();
        return it->second;
    }
}

extern void Setup();
extern void Clear();

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR lpCmdLine, int nCmdShow )
{
    using namespace std;
    using namespace wink;

    gInstance    = hInstance;
    gCommandLine = lpCmdLine;
    gShowMode    = nCmdShow;

    int result = 0;

    try
    {
        App::Init();
        Setup();
        App::Run();
    }
    catch( const Error& error )
    {
        error.ShowMessage();
        result = -1;
    }
    catch( const exception& error )
    {
        MessageBoxA( HWND_DESKTOP, error.what(), "Error!", MB_OK | MB_ICONERROR );
        result = -1;
    }
    catch( ... )
    {
        ErrorUnknown().ShowMessage();
        result = -1;
    }

    Clear();

    return result;
}
